package com.ruoyi.cert.service.impl;

import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import com.doudou.core.container.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.doudou.core.file.FileUtil;
import com.doudou.core.thread.ExecutorUtil;
import com.doudou.minio.Minio;
import com.github.pagehelper.PageHelper;
import com.ruoyi.cert.domain.CertType;
import com.ruoyi.cert.entity.CertVO;
import com.ruoyi.cert.service.ICertOrganizationService;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.aliyun.MySms;
import com.ruoyi.common.utils.img.ImageUtils;
import com.ruoyi.common.utils.img.QrCode;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.sql.SqlUtil;
import com.ruoyi.ex.domain.ExStudyReload;
import com.ruoyi.ex.mapper.ExStudyReloadMapper;
import com.ruoyi.pv.domain.PvClassScore;
import com.ruoyi.pv.domain.PvUserInfo;
import com.ruoyi.pv.service.IPvClassScoreService;
import com.ruoyi.pv.service.IPvStudentRecordService;
import com.ruoyi.system.mapper.StudentMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.cert.domain.CertOrganization;
import com.ruoyi.cert.mapper.CertMapper;
import com.ruoyi.cert.domain.Cert;
import com.ruoyi.cert.service.ICertService;

import static com.ruoyi.common.utils.file.FileUtils.stringToFile;
import static com.ruoyi.common.utils.file.FileUtils.getFileName;
import static com.ruoyi.common.utils.file.FileUtils.getNameSuffix;

/**
 * 证书Service业务层处理
 *
 * @author mxh
 * @date 2023-03-26
 */
@Service
public class CertServiceImpl implements ICertService {
    private static final Log log = LogFactory.get(CertServiceImpl.class);
    @Autowired private CertMapper certMapper;
    @Autowired private ExStudyReloadMapper exStudyReloadMapper;
    @Autowired private IPvClassScoreService classScoreService;
    @Autowired private ISysUserService userService;
    @Autowired private Minio minio;
    @Autowired private ICertOrganizationService certOrganizationService;
    @Autowired private ISysConfigService configService;
    @Autowired private IPvStudentRecordService studentRecordService;
//    @Autowired private MySms sms;
    @Autowired private StudentMapper studentMapper;


    /**
     * 默认上传的地址
     */
    private static String defaultBaseDir = RuoYiConfig.getProfile() + "/cert/";

    /**
     * 查询证书
     *
     * @param id 证书主键
     * @return 证书
     */
    @Override
    public Cert selectCertById(Long id) {
        return certMapper.selectCertById(id);
    }

    @Override
    public Cert selectCertByUserId(Long userId) {
        return certMapper.selectCertByUserId(userId);
    }

    /**
     * 查询证书列表
     *
     * @param cert 证书
     * @return 证书
     */
    @Override
    public List<Cert> selectCertList(Cert cert) {
        PageUtils.startPage();

        PageDomain pageDomain = TableSupport.buildPageRequest();
        String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
        Boolean reasonable = pageDomain.getReasonable();
        PageHelper.startPage(cert.getPageNum(), cert.getPageSize(), orderBy).setReasonable(reasonable);

        String nickName = cert.getUserName();
        if (StrUtil.isNotBlank(nickName)) {
            SysUser student = studentMapper.getStudentUserName(nickName);
            if (student != null) {
                cert.setStudentId(student.getUserId());
            } else {
                // 可根据业务需求处理查询不到学生的情况
                cert.setStudentId(-1L);
            }
        }else{
            cert.setStudentId(null);
        }
        return certMapper.selectCertList(cert);
    }


    /**
     * 查询证书列表
     *
     * @param cert 证书
     * @return 证书
     */
    @Override
    public List<Cert> selectAppletCert(Cert cert) {
        PageUtils.startPage();

        // 获取分页参数，优先使用cert对象中的值，如果没有则使用TableSupport获取的值
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = cert.getPageNum() != null ? cert.getPageNum() : pageDomain.getPageNum();
        Integer pageSize = cert.getPageSize() != null ? cert.getPageSize() : pageDomain.getPageSize();

        // 如果仍然为null，使用默认值
        if (pageNum == null) pageNum = 1;
        if (pageSize == null) pageSize = 100;

        System.out.println(cert.getPageNum());
        System.out.println(cert.getPageSize());

        String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
        Boolean reasonable = pageDomain.getReasonable();
        PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);

        List<Cert> certs = certMapper.selectCertList(cert);
        String userName = cert.getUserName();
        if (StrUtil.isNotBlank(userName)) {
            List<SysUser> studentList = studentMapper.getStudentList(SysUser.builder().nickName(userName).build());
            // 过滤出与studentList中用户ID匹配的证书
            certs = certs.stream()
                    .filter(c -> studentList.stream()
                            .anyMatch(s -> s.getUserId().equals(c.getStudentId())))
                    .collect(Collectors.toList());
        }
        return certs;
    }

    /**
     * 新增证书
     * @param cert 证书
     * @return 结果
     */
    @Transactional
    @Override
    public int insertCert(Cert cert) {
        return addCerts(cert,cert.getStudentIds());
    }

    /**
     * 插入语句调整后，需要对多线程环境做优化，加锁
     */
    private static final ReentrantLock LOCK = new ReentrantLock();

    @Transactional
    public int safetyInsertCert(Cert cert,SysUser student,PvUserInfo userInfo){
        if (cert == null){ return 0; }
        LOCK.lock();
        try{
            if (cert.getCertNo() == null){
                String postsNumber = generateNewPostsNumber();
                cert.setCertNo(postsNumber);
                //修改用户上岗证
                updateUserPostNumber(cert.getStudentId(),postsNumber);
            }
            int insertCert = certMapper.insertCert(cert);
            //修改用户培训记录的证书信息
            studentRecordService.updatePvStudentRecordCertByStudent(cert.getStudentId(), cert.getId());
            //发送短信
//            if (userInfo.isFirst()){
//                sms.sendFirstCert(student.getPhonenumber(),student.getNickName(),DateUtil.year(cert.getApprovalDate()),cert.getCertNo());
//            }else {
//                sms.sendReCert(student.getPhonenumber(),student.getNickName(),DateUtil.year(cert.getApprovalDate()));
//            }
            return insertCert;
        }catch (Exception e){
            log.error(e);
        }finally {
            LOCK.unlock();
        }
        return 0;
    }

    private int updateUserPostNumber(Long userId,String postNumber){
        return userService.updateUser(SysUser.builder().userId(userId).postsNumber(postNumber).build());
    }

    /**
     * 批量创建多个证书对象
     * 提前填充重复证书属性
     * @param cert 证书
     * @param studentIds 学生id
     */
    @Transactional
    public int addCerts(Cert cert,Long...studentIds){
        int count = 0;
        //封装相同参数
        cert.setStatus(1);
        cert.setCreateTime(DateUtils.getNowDate());
        if (cert.getCreateBy() == null) {
            cert.setCreateBy(SecurityUtils.getUsername());
        }
        //查询该培训班下的证书类型
        PvClassScore classScore = classScoreService.selectPvClassScoreById(cert.getClassId());
        //证书印章
        File zhangPhotoFile = null;
        //背景
        File backgroundPhotoFile = null;
        try{
            CertOrganization certOrganization = certOrganizationService.selectCertOrganizationById(classScore.getReCertOrganization());
            zhangPhotoFile = minioInputStreamToFile(getFileName(certOrganization.getCachet()), "zhang");
            backgroundPhotoFile = minioInputStreamToFile(configService.selectConfigByKey("background_template"), "background");
        }catch (Exception e){
            log.error(e);
        }

        // 优化1: 控制并发线程数，根据实际资源调整
        int maxThreads = Math.min(Runtime.getRuntime().availableProcessors(), 2); // 减少线程数
        int queueSize = Math.min(studentIds.length, 10); // 限制队列大小

        // 创建线程池
        ExecutorService executorService = ExecutorUtil.customThreadExecutor(maxThreads, maxThreads, 60L, queueSize);
        // 优化2: 分批处理，避免一次性提交太多任务
        int batchSize = 5; // 每批处理5个任务
        for (int i = 0; i < studentIds.length; i += batchSize) {
            int endIndex = Math.min(i + batchSize, studentIds.length);
            List<Future<Integer>> batchFutures = new ArrayList<>();

            // 提交一批任务
            for (int j = i; j < endIndex; j++) {
                Long studentId = studentIds[j];
                File finalZhangPhotoFile = zhangPhotoFile;
                File finalBackgroundPhotoFile = backgroundPhotoFile;
                Future<Integer> future = executorService.submit(() ->
                        addCerts(studentId, BeanUtil.copyProperties(cert, Cert.class),
                                classScore, finalZhangPhotoFile, finalBackgroundPhotoFile));
                batchFutures.add(future);
            }

            // 等待这一批任务完成
            for (Future<Integer> future : batchFutures) {
                try {
                    count += future.get(30, TimeUnit.SECONDS);
                } catch (TimeoutException e) {
                    log.error("证书生成超时: ", e);
                    // 可以选择取消任务
                    future.cancel(true);
                } catch (InterruptedException e) {
                    log.error("证书生成被中断: ", e);
                    Thread.currentThread().interrupt();
                } catch (Exception e) {
                    log.error("证书生成异常: ", e);
                }
            }
        }
        executorService.shutdown();
        return count;
    }

    @Transactional
    public int addCerts(Long studentId, Cert cert, PvClassScore classScore, File zhangPhotoFile, File backgroundPhotoFile) {
        SysUser student = userService.selectUserById(studentId);
        PvUserInfo userInfo = JSONUtil.parseObj(student.getUserInfo()).toBean(PvUserInfo.class);
        //查询考试成绩并校验考试成绩,如果不满足成绩的则不发证
//        ExStudyReload exStudyReload = exStudyReloadMapper.selectThisYearLastEx(studentId);
        ExStudyReload exStudyReload = exStudyReloadMapper.selectThisYearLast(studentId);
        if (exStudyReload != null && exStudyReload.getScore() != null && exStudyReload.getScore() < classScore.getFirstExScore()) {
            return 0;
        }
        CertType certType = classScore.getFirstCertTypeBean();
        cert.setCertTypeId(certType.getId());
        cert.setCertOrganizationId(classScore.getReCertOrganization());
        cert.setName(certType.getName() + certType.getType());
        //证书有效期,如果已经存在有效期，说明上层代码已经指定
        DateTime now = DateUtil.date();
        int nextYear = now.year() + 1;
        String endDate = nextYear + "-12-31 23:59:58"; //明年最后一天
        cert.setExp(DateUtil.parse(endDate));
        // 使用正则表达式提取年月
        String endDateStr = endDate.replaceAll("(\\d{4})-(\\d{2}).*", "$1年$2月");

        //先判断用户是否存在证书，且是最新的
        Cert selectCert = certMapper.selectCertByUserId(studentId);
        cert.setStudentId(studentId);
        cert.setCertNo(selectCert.getCertNo());
        //如果用户存在上岗证编号则直接使用，没有则生成证书编号
        if (ObjectUtil.isNotEmpty(selectCert)) {
            //判断证件是否吊销
            if (selectCert.getStatus().equals(2)) {
                //说明上岗证已经被注销此记录无用，生成新的上岗证记录
                cert.setCertNo(generateNewPostsNumber());
                String postsNumber = generateNewPostsNumber();
                updateUserPostNumber(cert.getStudentId(),postsNumber);
            }
            return insertCert(student, zhangPhotoFile, backgroundPhotoFile, userInfo,certType, cert,endDateStr);
        }else {
            //不存在证书 直接新增
            return insertCert(student, zhangPhotoFile, backgroundPhotoFile, userInfo,certType, cert,endDateStr);
        }
    }

    private Integer insertCert(SysUser student,
                               File zhangPhotoFile,
                               File backgroundPhotoFile,
                               PvUserInfo userInfo,
                               CertType certType,
                               Cert cert,
                               String endDateStr){
        try {
            if (StrUtil.isBlank(student.getAvatar())) {
                return 0;
            }
            String studentPhoto = getFileName(student.getAvatar());
            File studentPhotoFile = minioInputStreamToFile(studentPhoto, student.getNickName());
            //合成
            String image = ImageUtils.savePic(studentPhotoFile,
                    zhangPhotoFile,
                    backgroundPhotoFile,
                    student.getNickName(),
                    userInfo.getCommunity(),
                    certType.getName(),
                    certType.getType(),
                    endDateStr,
                    cert.getCertNo()
            );
            //证书地址
            File imageFile = stringToFile(image);
            String certImg = minio.uploadFile(imageFile);
            imageFile.delete();
            cert.setCertImg(certImg);
            //二维码地址 将证书作为二维码内容
            cert.setQrCode(QrCode.generateCodeBase(certImg, student.getNickName()));
            //防伪码
            cert.setSecurityCode(Base64.encode(certImg.getBytes()));
            cert.setStatus(1);
            return safetyInsertCert(cert, student, userInfo);
        } catch (Exception e) {
            log.error("生成证书图片失败: {}", e.getMessage(), e);
            return 0;
        }
    }
    /**
     * 将minio的文件转为本地文件   兜兜.png
     * @param minioName minio的文件名  sdfvsdvf-123123xcvasdfv-sdf123sdf12.png
     * @param newFileName 新的文件名，不带后缀    兜兜
     * @return file
     */
    private File minioInputStreamToFile(String minioName,String newFileName) throws Exception {
        File file = new File(defaultBaseDir + "/" + newFileName + FileUtil.getFileType(minioName));
        FileUtils.copyInputStreamToFile(minio.downloadFile(minioName), file);
        return file;
    }

    /**
     * 修改证书
     *
     * @param cert 证书
     * @return 结果
     */
    @Transactional
    @Override
    public int updateCert(Cert cert) {
        cert.setUpdateTime(DateUtils.getNowDate());
        cert.setUpdateBy(SecurityUtils.getUsername());
        if (cert.getStatus()==1){
            cert.setApprovalBy(SecurityUtils.getUsername());
            cert.setApprovalDate(DateUtils.getNowDate());
        }else if (cert.getStatus()==2){
            cert.setRevocationBy(SecurityUtils.getUsername());
            cert.setRevocationDate(DateUtils.getNowDate());
        }
        return certMapper.updateCert(cert);
    }

    @Transactional
    public int updateCert(Cert cert,boolean isGenerate,SysUser student,PvUserInfo userInfo) {
        if (cert == null){ return 0; }
        LOCK.lock();
        try{
            if (isGenerate){
                String postsNumber = generateNewPostsNumber();
                cert.setCertNo(generateNewPostsNumber());
                //修改用户上岗证
                updateUserPostNumber(cert.getStudentId(),postsNumber);
            }
            int updateCert = certMapper.updateCert(cert);
            //修改用户培训记录的证书信息
            studentRecordService.updatePvStudentRecordCertByStudent(cert.getStudentId(), cert.getId());
            //发送短信 todo
//            if (userInfo.isFirst()){
//                sms.sendFirstCert(student.getPhonenumber(),student.getNickName(),DateUtil.year(cert.getApprovalDate()),cert.getCertNo());
//            }else {
//                sms.sendReCert(student.getPhonenumber(),student.getNickName(),DateUtil.year(cert.getApprovalDate()));
//            }
            return updateCert;
        }catch (Exception e){
            log.error(e);
        }finally {
            LOCK.unlock();
        }
        return 0;
    }

    /**
     * 批量删除证书
     *
     * @param ids 需要删除的证书主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteCertByIds(Long[] ids) {
        return certMapper.deleteCertByIds(ids);
    }

    /**
     * 删除证书信息
     *
     * @param id 证书主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteCertById(Long id) {
        return certMapper.deleteCertById(id);
    }

    @Override
    public int resetCertNo(Long userId) {
        return certMapper.resetCertNo(userId);
    }

    @Override
    public List<Cert> selectCertListExport(Cert cert) {
        List<CertVO> certVOS = certMapper.selectCertListExport(cert);

        ArrayList<Cert> certs = new ArrayList<>();
        certVOS.stream().forEach(certVO -> {
            Cert certDto = new Cert();
            certDto.setCertNo(certVO.getCertNo());
            certDto.setIssuanceDate(certVO.getIssuanceDate());
            certDto.setCertName(certVO.getCertName());
            certDto.setClassId(certVO.getClassId());
            certDto.setStudentId(certVO.getStudentId());
            certDto.setDeptId(certVO.getDeptId());
            certDto.setUserName(certVO.getUserName());
            certDto.setClassName(certVO.getClassName());
            certDto.setOrgName(certVO.getOrgName());
            certDto.setStatus(certVO.getStatus());
            certs.add(certDto);
        });
        return certs;
    }

    public String generateNewPostsNumber() {
        String number = certMapper.generateNewPostsNumber();
        if (number.length() == 1){
            number = "00" + number;
        }else if(number.length() == 2){
            number = "0" + number;
        }else{
            throw new ServiceException("生成上岗证失败");
        }
        return DateTime.now().year() + number;
    }

}
